package org.unidata.mdm.data.service.impl;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.unidata.mdm.core.service.MetaModelService;
import org.unidata.mdm.core.type.model.EntityElement;
import org.unidata.mdm.core.type.timeline.Timeline;
import org.unidata.mdm.data.context.GetRelationsTimelineRequestContext;
import org.unidata.mdm.data.type.data.OriginRelation;
import org.unidata.mdm.data.type.keys.RecordKeys;
import org.unidata.mdm.data.type.merge.MergeRelationMasterState;
import org.unidata.mdm.meta.configuration.Descriptors;

/**
 * @author Mikhail Mikhailov on May 5, 2020
 */
@Component
public class MergeRelationsComponent {

    private final MetaModelService metaModelService;

    private final CommonRelationsComponent commonRelationsComponent;

    @Autowired
    public MergeRelationsComponent(
            final MetaModelService metaModelService,
            final CommonRelationsComponent commonRelationsComponent) {
        super();
        this.metaModelService = metaModelService;
        this.commonRelationsComponent = commonRelationsComponent;
    }

    public MergeRelationMasterState getMasterState(final RecordKeys master) {

        final String entityName = master.getEntityName();
        Set<String> to = metaModelService.instance(Descriptors.DATA)
                .getRegister(entityName)
                .getIncomingRelations()
                .keySet()
                .stream()
                .map(EntityElement::getName)
                .collect(Collectors.toSet());

        Set<String> from = metaModelService.instance(Descriptors.DATA)
                .getRegister(entityName)
                .getOutgoingRelations()
                .keySet()
                .stream()
                .map(EntityElement::getName)
                .collect(Collectors.toSet());

        return getMasterState(master, from, to);
    }

    public MergeRelationMasterState getMasterState(final RecordKeys master, Set<String> from, Set<String> to) {
        return new MergeRelationMasterState(
                loadFromMasterTimelines(master, from),
                loadToMasterTimelines(master, to));
    }

    private Map<String, List<Timeline<OriginRelation>>> loadToMasterTimelines(RecordKeys master, Set<String> toFilter) {

        if (CollectionUtils.isEmpty(toFilter)) {
            return Collections.emptyMap();
        }

        GetRelationsTimelineRequestContext masterCtx = GetRelationsTimelineRequestContext.builder()
                .fetchByToSide(true)
                .relationNames(toFilter.toArray(String[]::new))
                .etalonKey(master.getEtalonKey())
                .build();

        return commonRelationsComponent.loadTimelines(masterCtx);
    }

    private Map<String, List<Timeline<OriginRelation>>> loadFromMasterTimelines(RecordKeys master, Set<String> fromFilter) {

        if (CollectionUtils.isEmpty(fromFilter)) {
            return Collections.emptyMap();
        }

        GetRelationsTimelineRequestContext masterCtx = GetRelationsTimelineRequestContext.builder()
                .fetchByToSide(false)
                .relationNames(fromFilter.toArray(String[]::new))
                .etalonKey(master.getEtalonKey())
                .build();

        return commonRelationsComponent.loadTimelines(masterCtx);
    }
}
